#include "stc8g.h"
#include "bmp280.h"
#include "i2c.h"
#include "type.h"
// BMP280寄存器地址定义
#define BMP280_ADDR          0x77    // I2C设备地址
#define BMP280_REG_ID     0xD0
#define BMP280_REG_DIG_T1     0x88    // 温度校准参数起始地址
#define BMP280_REG_CTRL_MEAS  0xF4    // 控制测量寄存器
#define BMP280_REG_CONFIG     0xF5    // 配置寄存器
#define BMP280_REG_PRESSURE   0xF7    // 气压数据寄存器（连续3字节）

// 校准参数结构体（根据BMP280数据手册定义，16位参数）
typedef struct {
    uint16_t dig_T1;  // 无符号温度校准参数
    int16_t  dig_T2;  // 有符号温度校准参数
    int16_t  dig_T3;  // 有符号温度校准参数
    uint16_t dig_P1;  // 无符号气压校准参数
    int16_t  dig_P2;  // 有符号气压校准参数
    int16_t  dig_P3;  // 有符号气压校准参数
    int16_t  dig_P4;  // 有符号气压校准参数
    int16_t  dig_P5;  // 有符号气压校准参数
    int16_t  dig_P6;  // 有符号气压校准参数
    int16_t  dig_P7;  // 有符号气压校准参数
    int16_t  dig_P8;  // 有符号气压校准参数
    int16_t  dig_P9;  // 有符号气压校准参数
} bmp280_calib_t;

static xdata bmp280_calib_t calib_data;  // 存储校准参数


static void DelayMs(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++) {
            _nop_();
        }
    }
}


// 私有函数：向BMP280写单个字节
static void bmp280_write_reg(uint8_t reg, uint8_t _data) {
    I2C_Start();
    I2C_WriteByte(BMP280_ADDR << 1);  // 写地址（最低位0表示写）
    I2C_WriteByte(reg);
    I2C_WriteByte(_data);
    I2C_Stop();
}

// 私有函数：从BMP280读多个字节
static void bmp280_read_regs(uint8_t reg, uint8_t *buf, uint8_t len) {
    I2C_Start();
    I2C_WriteByte(BMP280_ADDR << 1);  // 写地址
    I2C_WriteByte(reg);
    I2C_Stop();
    DelayMs(1);
    I2C_Start();
    I2C_WriteByte((BMP280_ADDR << 1) | 0x01);  // 读地址（最低位1表示读）
    while (len--) {
        *buf = I2C_ReadByte(len ? 1 : 0);  // 非最后一个字节发送ACK，最后一个发送NACK
        buf++;
    }
    I2C_Stop();
}

// 读取校准参数（初始化时调用）
static void bmp280_read_calibration(void) {
    uint8_t buf[24];
    bmp280_read_regs(BMP280_REG_DIG_T1, buf, 24);

    // 解析温度校准参数（16位无符号/有符号）
    calib_data.dig_T1 = (buf[1] << 8) | buf[0];
    calib_data.dig_T2 = (buf[3] << 8) | buf[2];
    calib_data.dig_T3 = (buf[5] << 8) | buf[4];

    // 解析气压校准参数（16位无符号/有符号）
    calib_data.dig_P1 = (buf[7] << 8) | buf[6];
    calib_data.dig_P2 = (buf[9] << 8) | buf[8];
    calib_data.dig_P3 = (buf[11] << 8) | buf[10];
    calib_data.dig_P4 = (buf[13] << 8) | buf[12];
    calib_data.dig_P5 = (buf[15] << 8) | buf[14];
    calib_data.dig_P6 = (buf[17] << 8) | buf[16];
    calib_data.dig_P7 = (buf[19] << 8) | buf[18];
    calib_data.dig_P8 = (buf[21] << 8) | buf[20];
    calib_data.dig_P9 = (buf[23] << 8) | buf[22];
}

// 初始化BMP280
void bmp280_init(void) {
    // 读取校准参数
    bmp280_read_calibration();

    // 配置控制测量寄存器：16倍过采样气压，正常模式（0x57 = 0b01010111）
    bmp280_write_reg(BMP280_REG_CTRL_MEAS, 0x57);

    // 配置寄存器：IIR滤波器系数16，待机时间1000ms（0xA0 = 0b10100000）
    // bmp280_write_reg(BMP280_REG_CONFIG, 0x14);
}



// 读取原始气压值（20位数据）
static uint32_t bmp280_read_raw_pressure(void) {
    uint8_t buf[3];
    bmp280_read_regs(BMP280_REG_PRESSURE, buf, 3);
    return ((uint16_t)buf[0] << 12) | ((uint16_t)buf[1] << 4) | (buf[2] >> 4);
}

// 气压补偿算法（32位整数版本，适配C51）
int32_t bmp280_compensate_pressure(int32_t adc_P) {
    // 将16位校准参数转换为32位以扩展运算范围
    uint16_t dig_P1 = calib_data.dig_P1;
    int16_t dig_P2 = calib_data.dig_P2;
    int16_t dig_P3 = calib_data.dig_P3;
    int16_t dig_P4 = calib_data.dig_P4;
    int16_t dig_P5 = calib_data.dig_P5;
    int16_t dig_P6 = calib_data.dig_P6;
    int16_t dig_P7 = calib_data.dig_P7;
    int16_t dig_P8 = calib_data.dig_P8;
    int16_t dig_P9 = calib_data.dig_P9;

    int32_t var1, var2, p;

    // 步骤1：初始变量计算（避免64位乘法）
    var1 = ((((adc_P >> 3) - (dig_P1 << 1))) * dig_P2) >> 11;
    var2 = (((((adc_P >> 4) - dig_P1) * ((adc_P >> 4) - dig_P1)) >> 12) * dig_P3) >> 14;

    // 步骤2：合并初始变量（32位范围内）
    var1 = var1 + var2;
    var2 = (dig_P4 << 15) + ((var1 * dig_P5) >> 10);
    var1 = ((dig_P6 * var1) >> 13) + ((dig_P7 * var1) >> 18);

    // 步骤3：核心气压计算（32位位移替代64位除法）
    p = ((adc_P * 4096) + var1) * 6250;
    p = (p >> 15) / var2;

    // 步骤4：修正补偿（32位范围内调整）
    var1 = (dig_P9 * ((p >> 13) * (p >> 13))) >> 25;
    var2 = (dig_P8 * p) >> 19;
    p = p + ((var1 + var2 + dig_P7) >> 4);

    return p;
}

// 公共接口：获取气压值（单位：Pa）
void bmp280_get_pressure(uint32_t *pressure) {
    uint32_t raw_pressure = bmp280_read_raw_pressure();
    *pressure= bmp280_compensate_pressure((int32_t)raw_pressure);
}